# coding=utf-8
import numpy as np
from PIL import Image
import cv2  
import copy
import time

from baidu import division

#from matplotlib import pyplot as plt

def skin(path):
    test_image = cv2.imread(path)
    
    test_image = cv2.edgePreservingFilter(test_image, flags=1, sigma_s=50, sigma_r=0.5)
    # RGB到YCbCr色彩空间
    image_YCbCr = cv2.cvtColor(test_image, cv2.COLOR_RGB2YCrCb)

    # 返回行数，列数，通道个数
    shape = image_YCbCr.shape

    Kl, Kh = 125, 188
    Ymin, Ymax = 16, 235
    Wlcb, Wlcr = 23, 20
    Whcb, Whcr = 14, 10
    Wcb, Wcr = 46.97, 38.76
    # 椭圆模型参数
    Cx, Cy = 109.38, 152.02
    ecx, ecy = 1.60, 2.41
    a, b = 25.39, 14.03
    Theta = 2.53 / np.pi * 180
    
    start = time.time()
    # 每行
    for row in range(shape[0]):
        # 每列
        for col in range(shape[1]):
            Y = image_YCbCr[row, col, 0]
            CbY = image_YCbCr[row, col, 1]
            CrY = image_YCbCr[row, col, 2]
            if Y < Kl or Y > Kh:
                # 求Cb, Cr的均值
                if Y < Kl:
                    # 公式(7)
                    CbY_aver = 108 + (Kl - Y) * (118 - 108) / (Kl - Ymin)
                    # 公式(8)
                    CrY_aver = 154 - (Kl - Y) * (154 - 144) / (Kl - Ymin)
                    # 公式(6)
                    WcbY = Wlcb + (Y - Ymin) * (Wcb - Wlcb) / (Kl - Ymin)
                    WcrY = Wlcr + (Y - Ymin) * (Wcr - Wlcr) / (Kl - Ymin)
                elif Y > Kh:
                    # 公式(7)
                    CbY_aver = 108 + (Y - Kh) * (118 - 108) / (Ymax - Kh)
                    # 公式(8)
                    CrY_aver = 154 + (Y - Kh) * (154 - 132) / (Ymax - Kh)
                    # 公式(6)
                    WcbY = Whcb + (Ymax - Y) * (Wcb - Whcb) / (Ymax - Kh)
                    WcrY = Whcr + (Ymax - Y) * (Wcr - Whcr) / (Ymax - Kh)
                # 求Cb(Kh), Cr(Kh)的均值
                CbKh_aver = 108 + (Kh - Kh) * (118 - 108) / (Ymax - Kh)
                CrKh_aver = 154 + (Kh - Kh) * (154 - 132) / (Ymax - Kh)
                # 公式(5)
                Cb = (CbY - CbY_aver) * Wcb / WcbY + CbKh_aver
                Cr = (CrY - CrY_aver) * Wcr / WcrY + CrKh_aver
            else:
                # 公式(5)
                Cb = CbY
                Cr = CrY
            # Cb，Cr代入椭圆模型
            cosTheta = np.cos(Theta)
            sinTehta = np.sin(Theta)
            matrixA = np.array([[cosTheta, sinTehta], [-sinTehta, cosTheta]], dtype=np.double)
            matrixB = np.array([[Cb - Cx], [Cr - Cy]], dtype=np.double)
            # 矩阵相乘
            matrixC = np.dot(matrixA, matrixB)
            x = matrixC[0, 0]
            y = matrixC[1, 0]
            ellipse = (x - ecx) ** 2 / a ** 2 + (y - ecy) ** 2 / b ** 2
            if ellipse <= 1:
                # 白
                image_YCbCr[row, col] = [1, 1, 1]
                # 黑
            else:
                image_YCbCr[row, col] = [0, 0, 0]
    print '耗时', time.time()-start
    return image_YCbCr



class Beauty():
    """face object"""
    def __init__(self, path, newpath=False):
        self.path = path  # img path
        self.newpath = newpath  # save path
        self.current_img =  cv2.imread(path)
        self.new_img = None
        #self.skin_table = skin(path)
        self.skin_table = None
        self.outline = division(path)

    def generate_skin(self):
        """genrate skin table"""
        #self.skin_table = skin(self.path)
        #return
        rows,cols,channels = self.current_img.shape
        current_img = copy.deepcopy(self.current_img)  # get img data
        current_img = cv2.edgePreservingFilter(current_img, flags=1, sigma_s=50, sigma_r=0.5)
        self.skin_table = np.ones(self.current_img.shape, np.uint8)  # get data is 0 matrix
        #self.skin_table = copy.deepcopy(self.current_img)
        # Skin is identified by pixels
        for r in range(rows):  
            for c in range(cols):  
                # get pixel value         
                B = current_img.item(r,c,0)  
                G = current_img.item(r,c,1)  
                R = current_img.item(r,c,2)  
                # non-skin area if skin equals 0, skin area equals 1.         
                if (abs(R - G) > 15) and (R > G) and (R > B):  
                    if (R > 95) and (G > 40) and (B > 20) and (max(R,G,B) - min(R,G,B) > 15):
                        pass
                    elif (R > 220) and (G > 210) and (B > 170):  
                        pass
                else:
                    self.skin_table.itemset((r,c,2),0)  
                    self.skin_table.itemset((r,c,1),0)
                    self.skin_table.itemset((r,c,0),0)
        self.skin_table = np.uint8(self.outline * self.skin_table)
        print "皮肤识别完成"

    def buffing2(self, grade=3):
        # first judge skin is exist
        if self.skin_table == None:
            self.generate_skin()
        # buffing
        value = grade * 0.05
        current_img = cv2.edgePreservingFilter(self.current_img, flags=1, sigma_s=50, sigma_r=value)
        imgskin_c = np.uint8(-(self.skin_table - 1))
        skin = current_img * self.skin_table
        print "磨皮算法完成"
        #cv2.imwrite('skin.jpg', self.current_img * self.skin_table)
        skin = self.white_skin(skin, 8) # 磨皮中插入美白算法
        self.new_img = np.uint8(self.current_img * imgskin_c + skin)
        cv2.imwrite(self.newpath, self.new_img)
        print "美白并组装图片完成修图"
    
    def buffing(self, grade=3, b_skin=False):
        # first judge skin is exist
        if self.skin_table == None:
            self.generate_skin()
        # buffing
        value = grade * 0.05
        current_img = self.white_skin2(self.current_img, 10) # 磨皮中插入美白算法
        current_img = cv2.edgePreservingFilter(current_img, flags=1, sigma_s=50, sigma_r=value)
        imgskin_c = np.uint8(-(self.skin_table - 1))
        skin = current_img * self.skin_table
        self.new_img = np.uint8(self.current_img * imgskin_c + skin)
        print "磨皮算法完成"
        if b_skin:
            cv2.imwrite(self.newpath, skin)
        else:
            cv2.imwrite(self.newpath, self.new_img)
        print "识别皮肤美白并组装图片完成修图"
    
    def white_skin(self, skin, power=5):
        """
        power : 0 ~ 100
        """
        rows,cols,channels = skin.shape
        for r in range(rows):  
            for c in range(cols):  
                B = skin.item(r,c,0)  
                G = skin.item(r,c,1)  
                R = skin.item(r,c,2)  
                max_value = max([R, G, B])
                degree = 255 - max_value - power
                if degree >= 0: degree = power
                else: degree = 255 - max_value
                skin.itemset((r,c,0), B + degree) 
                skin.itemset((r,c,1), G + degree)
                skin.itemset((r,c,2), R + degree)
        return skin
    
    def white_skin2(self, skin, power=5):
        """
        power : 0 ~ 100
        """
        power = power / 100.0
        rows,cols,channels = skin.shape
        for r in range(rows):  
            for c in range(cols):  
                B = skin.item(r,c,0)  
                G = skin.item(r,c,1)  
                R = skin.item(r,c,2)  
                max_value = max([R, G, B])
                degree = 255 - max_value - int(max_value*power)
                if degree >= 0: degree = int(max_value*power)
                else: degree = 255 - max_value
                skin.itemset((r,c,0), B + degree) 
                skin.itemset((r,c,1), G + degree)
                skin.itemset((r,c,2), R + degree)
        return skin

    def show(self, img="Default"):
        if img == "Default":
            img = self.new_img
        print('正在加载')
        cv2.imshow('image', img)
        cv2.imshow('old', self.current_img)
        #cv2.imwrite('python3.png',img)
        cv2.waitKey(0)
        cv2.destroyAllWindows()


#b = Beauty(path="a.jpg", newpath='new.jpg')
#b.buffing(grade=5)
#b.show()
